package com.yn.pet.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yn.common.utils.DateUtils;
import com.yn.pet.bus.dao.DeviceSimDao;
import com.yn.pet.bus.dao.LocationRealtimeDao;
import com.yn.pet.bus.entity.LocationRealtime;
import com.yn.pet.bus.enums.LocationTypeEnum;
import com.yn.pet.controller.pc.response.ImmediateLocationResponse;
import com.yn.pet.controller.pc.response.LocationRealtimeResponse;
import com.yn.pet.controller.tcpserver.PetServer808Service;
import com.yn.pet.bus.dao.DeviceInfoDao;
import com.yn.pet.bus.entity.DeviceInfo;
import com.yn.pet.bus.enums.DeviceStatusEnum;
import com.yn.pet.protocol.basics.JTMessage;
import com.yn.pet.server.commons.util.StrUtils;
import com.yn.pet.service.LocationRealtimeService;
import io.swagger.models.auth.In;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.util.*;
import java.util.stream.IntStream;

/**
 * 实时定位信息表(LocationRealtime)表服务实现类
 *
 * @author makejava
 * @since 2025-07-09 12:00:47
 */
@Service("locationRealtimeService")
@RequiredArgsConstructor
@Slf4j
public class LocationRealtimeServiceImpl extends ServiceImpl<LocationRealtimeDao, LocationRealtime> implements LocationRealtimeService {
    private final DeviceInfoDao deviceInfoDao;
    private final DeviceSimDao deviceSimDao;
    private final PetServer808Service petServer808Service;

    /**
     * 查询实时信息定位列表_某设备的最新定位信息
     *
     * @param deviceId 设备ID
     * @return 实时信息定位列表_某设备的最新定位信息
     */
    @Override
    public LocationRealtime getLatestLocationByDeviceId(Long deviceId) {
        if (deviceId == null) {
            return null;
        }
        LambdaQueryWrapper<LocationRealtime> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(LocationRealtime::getDeviceId, deviceId)
                .orderByDesc(LocationRealtime::getCreateTime).last("limit 1");
        return this.getOne(queryWrapper);
    }

    /**
     * 查询实时信息定位列表_某一天的定位信息
     *
     * @param deviceId     设备ID
     * @param time         时间
     * @param locationTypes 定位类型
     * @return List<LocationRealtimeResponse>
     */
    @Override
    public List<LocationRealtimeResponse> listInfoByTime(String time, Long deviceId, String locationTypes) {
        //将数组字符串转为Integer
        ArrayList<Integer> locationTypeList = new ArrayList<>();
        if(StringUtils.isNotBlank(locationTypes)){
            String[] split = locationTypes.split(",");
            for (String s : split) {
                locationTypeList.add(Integer.parseInt(s));
            }
        }

        //得到今天开始时间
        Date startTime = DateUtils.getTodayStart();
        Date endTime = DateUtils.getTodayEnd();
        if (StrUtils.isNotBlank(time)) {
            startTime = DateUtils.parseDayStart(time);
            endTime = DateUtils.parseDayEnd(time);
        }
        LambdaQueryWrapper<DeviceInfo> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(DeviceInfo::getDeviceId, deviceId);
        DeviceInfo deviceInfo = deviceInfoDao.selectOne(queryWrapper);
        String imei = deviceInfo.getImei();
        //构建查询条件
        LambdaQueryWrapper<LocationRealtime> lambdaQuery = Wrappers.lambdaQuery();
        lambdaQuery.eq(LocationRealtime::getDeviceId, deviceId)
                .and(wrapper -> wrapper
                        .eq(LocationRealtime::getLocationType, LocationTypeEnum.GPS.getCode())
                        .or()
                        .in(CollectionUtils.isNotEmpty(locationTypeList),LocationRealtime::getLocationType, locationTypeList)
                );
        lambdaQuery.between(LocationRealtime::getGpsTime, startTime, endTime);
        //lambdaQuery.groupBy(LocationRealtime::getDetailedLocation);
        lambdaQuery.orderByAsc(LocationRealtime::getGpsTime);
        lambdaQuery.eq(LocationRealtime::getDataStatus, 1);
        List<LocationRealtime> realtimeList = this.list(lambdaQuery);
        if (CollectionUtils.isEmpty(realtimeList)) {
            log.info("没有查询到历史轨迹数据");
            return new ArrayList<>();
        }
        log.info("查询到{}条历史轨迹数据", realtimeList.size());
        //使用快慢指针对数据进行过滤
         realtimeList = filterLocation1(realtimeList);
         log.info("经过最终过滤后，剩余{}条历史轨迹数据", realtimeList.size());
        if (CollectionUtils.isEmpty(realtimeList)) {
            log.info("没有查询到历史轨迹数据");
            return new ArrayList<>();
        }
        ArrayList<LocationRealtimeResponse> response = new ArrayList<>(realtimeList.size());
        for (LocationRealtime realtime : realtimeList) {
            LocationRealtimeResponse transform = LocationRealtimeResponse.transform(realtime);
            if(transform == null){
                continue;
            }
            transform.setImei(imei);
            if(StrUtils.isNotBlank(deviceInfo.getDeviceName())){
                transform.setDeviceName(deviceInfo.getDeviceName());
            }else{
                transform.setDeviceName(deviceInfo.getImei());
            }
            response.add(transform);
        }
        return response;
    }

    /**
     * 获取实时信息_立即定位
     *
     * @param deviceId 设备ID
     * @return ImmediateLocationResponse
     */
    @Override
    public Boolean getLocationImmediate(Long deviceId,String sendTimeStr) {

        LambdaQueryWrapper<DeviceInfo> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(DeviceInfo::getDeviceId, deviceId);
        DeviceInfo deviceInfo = deviceInfoDao.selectOne(queryWrapper);
        if (deviceInfo == null) {
            throw new RuntimeException("设备不存在");
        }
        if (Objects.equals(deviceInfo.getDeviceStatus(), DeviceStatusEnum.OFFLINE.getCode())) {
            throw new RuntimeException("设备已离线,无法立即定位");
        }
        LocationRealtime location = getLatestLocationByDeviceId(deviceId);
        if(location == null){
            throw new RuntimeException("设备未定位");
        }
        return location.getCreateTime().getTime() >= DateUtils.parseDate(sendTimeStr).getTime();
    }

    /**
     * 根据位置id获取设备实时位置
     * @param locationId 位置id
     * @return LocationRealtime
     */
    @Override
    public LocationRealtime getByLocationId(Long locationId) {
        if(locationId == null){
            return null;
        }
        LambdaQueryWrapper<LocationRealtime> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(LocationRealtime::getLocationId, locationId);

        return this.getOne(queryWrapper);
    }

    /**
     * 获取指定时间的那一天指定设备位置信息
     * @param time  时间
     * @param deviceId 设备id
     * @return List<LocationRealtime>
     */
    @Override
    public List<LocationRealtime> listOneDayLocation(Date time, Long deviceId) {
        if(time == null || deviceId == null){
            return Collections.emptyList();
        }
        //获取该天的开始时间
        Date startTime = DateUtils.parseDayStart(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, time));
        //获取该天的结束时间
        Date todayEnd = DateUtils.parseDayEnd(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, time));
        LambdaQueryWrapper<LocationRealtime> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(LocationRealtime::getDeviceId, deviceId)
                .between(LocationRealtime::getGpsTime, startTime, todayEnd)
                .orderByDesc(LocationRealtime::getCreateTime);
        return this.list(queryWrapper);
    }

    /**
     * 立即定位
     * @param deviceId 设备id
     */
    @Override
    public String getLocation(Long deviceId) {
        LambdaQueryWrapper<DeviceInfo> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(DeviceInfo::getDeviceId, deviceId);
        DeviceInfo deviceInfo = deviceInfoDao.selectOne(queryWrapper);
        if (deviceInfo == null) {
            throw new RuntimeException("设备不存在");
        }
        if (Objects.equals(deviceInfo.getDeviceStatus(), DeviceStatusEnum.OFFLINE.getCode())) {
            throw new RuntimeException("设备已离线,无法立即定位");
        }
        //通过设备id获取sim卡号String imei = deviceInfo.getImei();
       /* LambdaQueryWrapper<DeviceSim> lambdaQuery = Wrappers.lambdaQuery();
        lambdaQuery.eq(DeviceSim::getDeviceId, deviceId);
        DeviceSim deviceSim = deviceSimDao.selectOne(lambdaQuery);
        if (deviceSim == null) {
            throw new RuntimeException("设备未绑定sim卡");
        }*/

        String clientId = deviceInfo.getImei();
        //向设备发送指令获取设备位置信息
        JTMessage t8201 = new JTMessage();
        t8201.setClientId(clientId);
        petServer808Service.T8201(t8201) .doOnSuccess(apiResult -> {
                    if (apiResult != null && apiResult.getData() != null) {
                            log.info("消息接收成功");
                    }else{
                        //走熔断了，后期设置重发
                        log.error("发送消息走熔断了");
                    }
                })
                .doOnError(throwable -> {
                    log.error("发送消息失败");
                    //逻辑删除，抛异常，用户看不见这条新增信息
                    throw new RuntimeException("网络错误，请稍后重试");
                })
                .subscribe(
                        result -> log.info("发送成功"),
                        throwable -> {
                            //  这里才是最终的错误处理入口
                            log.error("订阅失败", throwable);
                            // 可再次更新状态或做其他处理
                        }
                );
        return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, new Date());
    }
    /**
     * 计算两个经纬度之间的距离
     *
     * @param lat1 第一个点的纬度
     * @param lng1 第一个点的经度
     * @param lat2 第二个点的纬度
     * @param lng2 第二个点的经度
     * @return 距离（单位：米）
     */
    public static double calculateDistance(double lat1, double lng1, double lat2, double lng2) {
        // 地球半径（米）
        final double EARTH_RADIUS = 6371000;

        // 将角度转换为弧度
        double lat1Rad = Math.toRadians(lat1);
        double lng1Rad = Math.toRadians(lng1);
        double lat2Rad = Math.toRadians(lat2);
        double lng2Rad = Math.toRadians(lng2);

        // 计算差值
        double latDiff = lat2Rad - lat1Rad;
        double lngDiff = lng2Rad - lng1Rad;

        // Haversine公式
        double a = Math.sin(latDiff / 2) * Math.sin(latDiff / 2)
                + Math.cos(lat1Rad) * Math.cos(lat2Rad)
                * Math.sin(lngDiff / 2) * Math.sin(lngDiff / 2);

        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        // 计算距离
        return EARTH_RADIUS * c;
    }
    public static void main(String[] args) {
        double distance = calculateDistance(34.687556, 113.676127, 34.686438, 113.675806);
        System.out.println("距离：" + distance + "米");
    }


    /**
     * 计算两个时间之间的差值，返回格式化的时间差字符串
     *
     * @param startDate 开始时间
     * @param endDate 结束时间
     * @return 格式化的时间差字符串
     */
    /**
     * 计算两个时间之间的差值，返回秒数
     *
     * @param startDate 开始时间
     * @param endDate 结束时间
     * @return 时间差秒数
     */
    public static long calculateTimeDifferenceInSeconds(Date startDate, Date endDate) {
        if (startDate == null || endDate == null) {
            return 0;
        }

        // 计算时间差（毫秒）
        long timeDifferenceMillis = Math.abs(endDate.getTime() - startDate.getTime());

        // 转换为秒数
        return timeDifferenceMillis / 1000;
    }

    public static List<LocationRealtime> filterLocation(List<LocationRealtime> locationList) {
        if (CollectionUtils.isEmpty(locationList)) {
            return Collections.emptyList();
        }
        List<LocationRealtime> filteredList = new ArrayList<>();

        int slow = 0;
        int fast = 1;
        locationList.add(locationList.get(0));
        while(fast < locationList.size()){
            LocationRealtime slowRealtime = locationList.get(slow);
            LocationRealtime fastRealtime = locationList.get(fast);
            //计算两者的create_time时间差(精确到秒)
            long   seconds = calculateTimeDifferenceInSeconds(slowRealtime.getCreateTime(), fastRealtime.getCreateTime());
            double distance = calculateDistance(slowRealtime.getLatitude(), slowRealtime.getLongitude(), fastRealtime.getLatitude(), fastRealtime.getLongitude());
            if (distance <= 100 && seconds <= 60) {
                filteredList.add(fastRealtime);
            }
            slow++;
            fast++;
        }
        return filteredList;
    }
    public static List<LocationRealtime> filterLocation1(List<LocationRealtime> locationList) {
        if (CollectionUtils.isEmpty(locationList)) {
            return Collections.emptyList();
        }

        List<LocationRealtime> filteredList = new ArrayList<>();
        // 添加第一个点作为起始点
        filteredList.add(locationList.get(0));

        int slow = 0;
        int fast = 1;

        while (fast < locationList.size()) {

            LocationRealtime slowRealtime = locationList.get(slow);
            LocationRealtime fastRealtime = locationList.get(fast);
            if(slowRealtime.getDetailedLocation().equals(fastRealtime.getDetailedLocation())){
                slow=fast;
                fast++;
                continue;
            }
            // 判断设备状态
            boolean isStaticSlow = DeviceStatusEnum.STATIC.getCode().equals(slowRealtime.getDeviceStatus());
            boolean isStaticFast = DeviceStatusEnum.STATIC.getCode().equals(fastRealtime.getDeviceStatus());
            boolean isStatic = isStaticSlow && isStaticFast;
            // 计算时间差和距离差
            long seconds = calculateTimeDifferenceInSeconds(slowRealtime.getCreateTime(), fastRealtime.getCreateTime());
            double distance = calculateDistance(slowRealtime.getLatitude(), slowRealtime.getLongitude(),
                    fastRealtime.getLatitude(), fastRealtime.getLongitude());

            // 判断定位类型
            Integer slowLocationType = slowRealtime.getLocationType();
            Integer fastLocationType = fastRealtime.getLocationType();

            if (isStatic) {
                // 静止状态下的处理逻辑
                if (isBothWifi(slowLocationType, fastLocationType)) {
                    // 两个都是WiFi定位，250米内只保留一个
                    if (distance > 100) {
                        filteredList.add(fastRealtime);
                        slow = fast; // 更新基准点
                    }
                } else if (hasBaseStation(slowLocationType, fastLocationType)) {
                    // 如果有基站定位，优先保留GPS或WiFi
                    if (isGpsOrWifi(fastLocationType)) {
                        filteredList.add(fastRealtime);
                        slow = fast;
                    }
                    // 如果都是基站
                    else if (isBaseStation(slowLocationType) && isBaseStation(fastLocationType)) {
                        //判断是否是最后一个元素，如果是，保存，如果不是继续寻找下一个节点，直到找到不是基站的
                        if (fast == locationList.size() - 1) {
                            filteredList.add(fastRealtime);
                        }
                    }
                } else {
                    // 都是GPS，根据距离和时间判断
                    if (distance > 100 && seconds > 60) {
                        filteredList.add(fastRealtime);
                        slow = fast;
                    }
                }//有一个是静止状态，另一个是移动状态(ABA问题，判断两次相隔时间，暂定30min)
            } else if(isStaticSlow || isStaticFast){
                //判断快指针是不是基站
                if(isBaseStation(fastLocationType)){
                    if (fast == locationList.size() - 1) {
                        filteredList.add(fastRealtime);
                    }
                }else if(seconds>60*30){
                    filteredList.add(fastRealtime);
                }
                slow=fast;
            }else{
                //如果是运动的状态，删除慢指针，保存快指针
                LocationRealtime lastLocation = filteredList.get(filteredList.size() - 1);
                double calculateDistance = calculateDistance(lastLocation.getLatitude(), lastLocation.getLongitude(),
                        fastRealtime.getLatitude(), fastRealtime.getLongitude());
                if(isBaseStation(fastLocationType)){
                    if (fast == locationList.size() - 1) {
                        filteredList.add(fastRealtime);
                    }
                }else if(fastRealtime.getLocationType().equals(LocationTypeEnum.GPS.getCode())){
                    if(fastRealtime.getSatelliteNum()>=15){
                        filteredList.add(fastRealtime);
                    }
                }else{
                    if(calculateDistance<50){
                        filteredList.add(fastRealtime);
                    }
                }
                slow = fast;
            }
            fast++;
        }
        //将最后一条数据强行加到结果中
        LocationRealtime newRealtime = filteredList.get(filteredList.size() - 1);
        LocationRealtime oldRealtime = locationList.get(locationList.size() - 1);
        if(!newRealtime.getDetailedLocation().equals(oldRealtime.getDetailedLocation())){
            filteredList.add(locationList.get(locationList.size() - 1));
        }
        return filteredList;
    }


    private static boolean isBothWifi(Integer type1, Integer type2) {
        // WiFi定位类型
        return type1 != null && type2 != null && type1.equals(LocationTypeEnum.WIFI.getCode()) && type2.equals(LocationTypeEnum.WIFI.getCode());
    }

    private static boolean hasBaseStation(Integer type1, Integer type2) {
        // 基站定位类型
        return  type1.equals(LocationTypeEnum.CELL.getCode()) || type2.equals(LocationTypeEnum.CELL.getCode());
    }

    private static boolean isBaseStation(Integer type) {
        // 基站定位类型
        return type.equals(LocationTypeEnum.CELL.getCode());
    }

    private static boolean isGpsOrWifi(Integer type) {
        // 假设GPS为0，WiFi为1
        return type != null && (type.equals(LocationTypeEnum.GPS.getCode()) || type.equals(LocationTypeEnum.WIFI.getCode()));
    }

}

